{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# How to create branches for parallel node execution\n",
        "\n",
        "LangGraph natively supports fan-out and fan-in using either regular edges or\n",
        "[conditionalEdges](/langgraphjs/reference/classes/langgraph.StateGraph.html#addConditionalEdges).\n",
        "\n",
        "This lets you run nodes in parallel to speed up your total graph execution.\n",
        "\n",
        "Below are some examples showing how to add create branching dataflows that work\n",
        "for you.\n",
        "\n",
        "## Setup\n",
        "\n",
        "First, install LangGraph.js\n",
        "\n",
        "```bash\n",
        "yarn add @langchain/langgraph @langchain/core\n",
        "```\n",
        "\n",
        "This guide will use OpenAI's GPT-4o model. We will optionally set our API key\n",
        "for [LangSmith tracing](https://smith.langchain.com/), which will give us\n",
        "best-in-class observability.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Branching: LangGraphJS\n"
          ]
        }
      ],
      "source": [
        "// process.env.OPENAI_API_KEY = \"sk_...\";\n",
        "\n",
        "// Optional, add tracing in LangSmith\n",
        "// process.env.LANGCHAIN_API_KEY = \"ls__...\"\n",
        "// process.env.LANGCHAIN_CALLBACKS_BACKGROUND = \"true\";\n",
        "process.env.LANGCHAIN_CALLBACKS_BACKGROUND = \"true\";\n",
        "process.env.LANGCHAIN_TRACING_V2 = \"true\";\n",
        "process.env.LANGCHAIN_PROJECT = \"Branching: LangGraphJS\";"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Fan out, fan in\n",
        "\n",
        "First, we will make a simple graph that branches out and back in. When merging\n",
        "back in, the state updates from all branches are applied by your **reducer**\n",
        "(the `aggregate` method below).\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {},
      "outputs": [],
      "source": [
        "import { END, START, StateGraph, Annotation } from \"@langchain/langgraph\";\n",
        "\n",
        "const StateAnnotation = Annotation.Root({\n",
        "  aggregate: Annotation<string[]>({\n",
        "    reducer: (x, y) => x.concat(y),\n",
        "  })\n",
        "});\n",
        "\n",
        "// Create the graph\n",
        "const nodeA = (state: typeof StateAnnotation.State) => {\n",
        "  console.log(`Adding I'm A to ${state.aggregate}`);\n",
        "  return { aggregate: [`I'm A`] };\n",
        "};\n",
        "const nodeB = (state: typeof StateAnnotation.State) => {\n",
        "  console.log(`Adding I'm B to ${state.aggregate}`);\n",
        "  return { aggregate: [`I'm B`] };\n",
        "};\n",
        "const nodeC = (state: typeof StateAnnotation.State) => {\n",
        "  console.log(`Adding I'm C to ${state.aggregate}`);\n",
        "  return { aggregate: [`I'm C`] };\n",
        "};\n",
        "const nodeD = (state: typeof StateAnnotation.State) => {\n",
        "  console.log(`Adding I'm D to ${state.aggregate}`);\n",
        "  return { aggregate: [`I'm D`] };\n",
        "};\n",
        "\n",
        "const builder = new StateGraph(StateAnnotation)\n",
        "  .addNode(\"a\", nodeA)\n",
        "  .addEdge(START, \"a\")\n",
        "  .addNode(\"b\", nodeB)\n",
        "  .addNode(\"c\", nodeC)\n",
        "  .addNode(\"d\", nodeD)\n",
        "  .addEdge(\"a\", \"b\")\n",
        "  .addEdge(\"a\", \"c\")\n",
        "  .addEdge(\"b\", \"d\")\n",
        "  .addEdge(\"c\", \"d\")\n",
        "  .addEdge(\"d\", END);\n",
        "\n",
        "const graph = builder.compile();\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "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"
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import * as tslab from \"tslab\";\n",
        "\n",
        "const representation = graph.getGraph();\n",
        "const image = await representation.drawMermaidPng();\n",
        "const arrayBuffer = await image.arrayBuffer();\n",
        "\n",
        "await tslab.display.png(new Uint8Array(arrayBuffer));"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Adding I'm A to \n",
            "Adding I'm B to I'm A\n",
            "Adding I'm C to I'm A\n",
            "Adding I'm D to I'm A,I'm B,I'm C\n",
            "Base Result:  { aggregate: [ \"I'm A\", \"I'm B\", \"I'm C\", \"I'm D\" ] }\n"
          ]
        }
      ],
      "source": [
        "// Invoke the graph\n",
        "const baseResult = await graph.invoke({ aggregate: [] });\n",
        "console.log(\"Base Result: \", baseResult);"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Conditional Branching\n",
        "\n",
        "If your fan-out is not deterministic, you can use\n",
        "[addConditionalEdges](/langgraphjs/reference/classes/index.StateGraph.html#addConditionalEdges)\n",
        "directly like this:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {},
      "outputs": [],
      "source": [
        "const ConditionalBranchingAnnotation = Annotation.Root({\n",
        "  aggregate: Annotation<string[]>({\n",
        "    reducer: (x, y) => x.concat(y),\n",
        "  }),\n",
        "  which: Annotation<string>({\n",
        "    reducer: (x: string, y: string) => (y ?? x),\n",
        "  })\n",
        "})\n",
        "\n",
        "// Create the graph\n",
        "const nodeA2 = (state: typeof ConditionalBranchingAnnotation.State) => {\n",
        "  console.log(`Adding I'm A to ${state.aggregate}`);\n",
        "  return { aggregate: [`I'm A`] };\n",
        "};\n",
        "const nodeB2 = (state: typeof ConditionalBranchingAnnotation.State) => {\n",
        "  console.log(`Adding I'm B to ${state.aggregate}`);\n",
        "  return { aggregate: [`I'm B`] };\n",
        "};\n",
        "const nodeC2 = (state: typeof ConditionalBranchingAnnotation.State) => {\n",
        "  console.log(`Adding I'm C to ${state.aggregate}`);\n",
        "  return { aggregate: [`I'm C`] };\n",
        "};\n",
        "const nodeD2 = (state: typeof ConditionalBranchingAnnotation.State) => {\n",
        "  console.log(`Adding I'm D to ${state.aggregate}`);\n",
        "  return { aggregate: [`I'm D`] };\n",
        "};\n",
        "const nodeE2 = (state: typeof ConditionalBranchingAnnotation.State) => {\n",
        "  console.log(`Adding I'm E to ${state.aggregate}`);\n",
        "  return { aggregate: [`I'm E`] };\n",
        "};\n",
        "\n",
        "// Define the route function\n",
        "function routeCDorBC(state: typeof ConditionalBranchingAnnotation.State): string[] {\n",
        "  if (state.which === \"cd\") {\n",
        "    return [\"c\", \"d\"];\n",
        "  }\n",
        "  return [\"b\", \"c\"];\n",
        "}\n",
        "\n",
        "const builder2 = new StateGraph(ConditionalBranchingAnnotation)\n",
        "  .addNode(\"a\", nodeA2)\n",
        "  .addEdge(START, \"a\")\n",
        "  .addNode(\"b\", nodeB2)\n",
        "  .addNode(\"c\", nodeC2)\n",
        "  .addNode(\"d\", nodeD2)\n",
        "  .addNode(\"e\", nodeE2)\n",
        "  // Add conditional edges\n",
        "  // Third parameter is to support visualizing the graph\n",
        "  .addConditionalEdges(\"a\", routeCDorBC, [\"b\", \"c\", \"d\"])\n",
        "  .addEdge(\"b\", \"e\")\n",
        "  .addEdge(\"c\", \"e\")\n",
        "  .addEdge(\"d\", \"e\")\n",
        "  .addEdge(\"e\", END);\n",
        "\n",
        "const graph2 = builder2.compile();\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "/9j/4AAQSkZJRgABAQAAAQABAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAGCANQDASIAAhEBAxEB/8QAHQABAAICAwEBAAAAAAAAAAAAAAYHBQgDBAkBAv/EAFQQAAEDBAADAwYHCwcJCAMAAAEAAgMEBQYRBxIhEzFBCBQWIlFVFRcyYZSy0QkjNTZCYnF3gZPhJDNScnSRsRglNEN1doKiszc4OUVWtLXSY5XB/8QAGwEBAAMBAQEBAAAAAAAAAAAAAAMEBQIBBgf/xAA5EQACAQIDAwgHCAMBAAAAAAAAAQIDEQQhMRJBkQUTFVFxocHwFCIyUmGBsTM0U2Jy0eHxQmOywv/aAAwDAQACEQMRAD8A9U0REAREQBERAEREBjHZPZmOLXXaha4HRBqWAg/3r56VWX3xQfSWfaqixCyW6ox6lkloKWSRxkLnvhaSTzu7zpZn0etfu2j/AHDPsWfW5RoUasqTi3strVbmbEeT7pPaLE9KrL74oPpLPtT0qsvvig+ks+1V36PWv3bR/uGfYno9a/dtH+4Z9ih6Vw/uS4o96O/N3FielVl98UH0ln2p6VWX3xQfSWfaq79HrX7to/3DPsT0etfu2j/cM+xOlcP7kuKHR35u4sT0qsvvig+ks+1PSqy++KD6Sz7VXfo9a/dtH+4Z9iej1r920f7hn2J0rh/clxQ6O/N3FielVl98UH0ln2p6VWX3xQfSWfaq79HrX7to/wBwz7E9HrX7to/3DPsTpXD+5Lih0d+buLOorpR3MPNHVwVYZrmMErX8u+7ej0XaVb8NKSCizDJ46eGOCPzWhPLEwNG+ao66Csha14ySlHRpPirmXVp81Nw6giIhEEREAREQBERAEREAREQBERAEREBTeFfizR/pk+u5ZxYPCvxZo/0yfXcs4vjcd97q/ql9WfYQ9lBQ6s4u4lQ5oMTlupN+5443U0VNNI2J8g3GySRrCxjnAggOcCQQVMVr/l3wrYeNsdXhFnyenutxuVFFfBJQF9kuFJyNbJUdsekcscfqgtLXEs1yuB2q9KCm2n1HNSTik0S3h35QFlzq4ZbSyU9XbfgGsqYjLNRVLYn08LWc0rpHRNY1xLj963zgDeiOqzmI8acNzmS4RWa8Geegg86ngnpJ6eVsPX741krGuezp8poI7vaqytVdlmFx8XrFaMeunpPX3KvvNjrzROfQTCSnjMX375AfzMI5HHv1voVGsOs1xl4nUV3hted1dPU4ncLbU3LJ4Z+Z1Y50MnII3/zTTyO1ytbG52g3mKsujB3ay6s/gQKpNWRYeXeVLiVr4cXPKsefU5HFTQxSxdlQVTIJe0cGtHbdiWgjZ5h3tI5XcpVpY3kdFllngulu8580mLgzzukmpZPVcWnccrWvHUHvA33joVR1Xgl6rvInocbo7ROy/Mx2k3bJIzFMZWCOR8ZY7RDyWuGj12VdWH5VHmVmbcYrbdLU1zizza8UT6ScEAd8bwDrr39x0VDUjBR9Vb3v7CSEpOXrdSM2iIqxYObh7+OmTf2Sh+tUKwlXvD38dMm/slD9aoVhL7yl9lT/AEx/5R8vivtpBERSFQIiIAiIgCIiAIiIAiIgCIiAIiICm8K/Fmj/AEyfXcsDV8CeHNfVzVVTguPVFTO90ksslthc57idlxJb1JJJ2rJi4R2umZyQXK8QRbJEcdZprdkkgDXzr9/FVQ++L39N/gsuvyc6ladWFW2029HvdzcWNo7KUkVd/k/cMv8A0BjZ/Ta4f/qprbLZSWW3U1BQU0VFRU0bYoKeBgZHGwDQa1o6AAeAWc+Kqh98Xv6b/BPiqoffF7+m/wAFXfJUpa1lwZ0sdRWkTGosl8VVD74vf03+CqLyfqWt4jXHijDeb3dHsx7Ma6y0PY1HJy00QjLA7p6zvWPVc9D/AO1cGddIUupllqL5Pwtw7NbgyuyDF7Req1kYhbUV9FHM9rASQ0OcCdbc46+cqdfFVQ++L39N/gnxVUPvi9/Tf4L1ckuLuqq4M8ePpPJplXngFw0LAw4FjhYCSG/BkOgTrZ+T8w/uUjxbCMewemmp8eslBZIJ3iSWK30zIWvdrWyGgbOlLfiqoffF7+m/wT4qqH3xe/pv8F0+SpyVnW+pysbRWaidPh7+OmTf2Sh+tUKwlH8YwqixWoraimnq6merbG2WSrm7Q6ZzcoHTp8t396kC21FQjGCd7JLgkjJrzVSo5reEREIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAtd/JD/DPHb9Y90+rEtiFrv5If4Z47frHun1YkBsQiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgC138kP8ADPHb9Y90+rEtiFrv5If4Z47frHun1YkBsQiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgNY/ugXAw8YOB9RcrfAJcgxYvuVLobdJBy/yiIfpa0P0OpMTR4rzf8kvghJx6422WwTQvdZKZ3n92eOgbSxkFzd+Be4tjHs59+C9grnxME8j4rDa/huIba6rlqBBSuPiGv5XOePaWtLfYSdgUvwE4TxeTrcMtrLLYKKs9Iq81T2xVxY6jgBJjpog6IBzGF79ElpOxvuCn5mW9pdrS8cvmWFh6sldRNokWFxzLKDJWStp3OhrINecUU+mzQ77iRvq06OnAlp0dE6KzSilFxdmQNOLswiIuTwIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAq+z65uvN1GPMP8gjiE9wAP8AO8xIjgPtaQHOcPEBrTtrnA2Cqma8y5Xlj367QXFrO7qGimg5R/d1/apqfqqU1qllxSLmEgp1c9x2mtDGhrQGtA0AB0AX1YnLbxBj+LXe51VwjtNPSUksz6+WPtG04awntC38rXfy+OteKpXg3xHy6s4oR47fau8XS0XKySXaiq75aaa3z80c0TD2bIHE9m5soOpGh4IHtVQ3pTUWl1l611NNzxVlC8QXOm9anm7vYTG72sdoBw/aNEAix8cvcWSWOiuULTG2ojDjE4gmN3c5hI6ba4Fp14hQNZnhQ8/ANxj/ANXHdKoR6GhoyFx/5nO/btWoetSd91u/z9TOx0FZT3k1REURjBERAEREAREQBERAEREAREQBERAEREAREQBFjrtfqS0U9Y+R5nnpaV9Y6jp9PqHxtB2Wx952Roe06CrJuS5txowHG73g803DkVVw7StZlFp56w0THO1yRc3KDJph6n5LyQ4EDYFlXTI6G1+dxOmFRX01I+uNup3B9VJE3vLI97Oz6o8NkDap/G8qqsrNPk9Vjt2xalyLboqG9MEdQyWLbG87ATymSFrHhvf6jx4AmyrXwtxWzZ9eM2o7LTxZVdomQVl06ulfGxrWhg2dNGmM2GgbLQTvQWeu1opL7b5qGuhE9NKBzN2WkEHYc1wILXAgEOBBBAIIIUkJJXUtHkTUajpTUiqcoxq35ljlzsV1h84ttxp30tRGHFpcx4IOiOoPXofBQ3F+CVBjWV23JJMjyG9XehpJaBk10q2SB9M/lPZOa2NrdBzGuDmgOJHrOcOisevxjIrDIWUtN6R0X5EjJo4apo/ova/lY4/nBzd+LRrZi+K5s/PJ7xBjlmrK+az18lsuBmkhhZS1TNc8TiXkkjY6sDh1709Hn/i012rx0+ZuKtRn6zZn7hXMt1HJUSBzg3QaxnVz3E6axo8XOJAA8SQF94bZlUWfIblhN3xu8Wqa3Uhu0mQVMbfgyr7QiSYRz772Pkc3lIB5Wb6BSXGsHfTVcdzvMrKqvYdwU0fWnpfnbsbe/XTnOum+UN2dyi42+lu9vqaGup4quiqonQT087A+OWNwIc1zT0IIJBB79r12hHYTv1+fP75mKxCqtRjoj7RV1NcqOGro6iKqpZmCSKeB4eyRpGw5rh0IPtC51VNbwku2CYljti4QXC2YZb7bczV1NBW0jquGrgke50sXMXc7Ory4aO9ta3bWrPWzinDX8Tr5hc9gvVuktlGyubeaul5LdVxEM5uym31c1ztEED5JPgoigThFxU1TDWU8c9PKyeCVofHLG4Oa9p6ggjoQfauVAEREAREQBERAEREAREQBERAEXUu10p7LbKyvq3PbT0sD6iXs43SP5GN5nFrGgucdDuAJPcAqx9N8r4vYPjeQcMZKWxUtZcN1r8ttszJvM2OcC6GMEbL+VvKT0LH721wQFmXS709noqupnL3imp31L4oWGSVzGDZ5WN25x8AAOp0FWfpTmHGPBscvnD+d2DR1Nw5q1uV2d5qzRsc4bji5gPvnK0gk9WP2HNI0pVauE+MWXiPec8pbe5uU3enZSVVc6okduFgaAxrC7kaNsaToDZGypegIfaOEuKWPiNes8pLSyPK7xCymq7iXuc50TGsaGNaTytH3thOgNkDe1MERAEREAVdcIbt8KVudt9AfQbzbI6qn7bsOy+G9Bn+cP5pnN2m9c3r75fllWKoXw4tebWyqyx2ZXiiu0FTe557G2jYGmltxDexhk1Gzb2kO2Tz949YoCaIiIAutc7bS3m21dvroGVVFVxPgngkG2yRuBa5pHsIJC7KICqH8JLxw4w3Hsd4P1trxW3W65GpqaK7U8lZHUU8j3OliDy/nbovJHXfqtbzNG9yS2cUKK5cSr1hTrReqKtttKysFxq6F0dBVREM2YZ+5xa5/KR06tdrejqZrrXO2015ttXb6yIT0dVC+CaIkgPY4FrhsdeoJHRAc8cjZY2vY4PY4BzXNOwQe4gr9KqJOF994Y4VjuO8H5LPZrfQXHtamkv8A29UySle5xkjY/mL2kc+29fyGjYG9yW18VbPdeJd4wVtNdKa9WylZWulqaCSOlqIXBm3xTEcj9F4aeo6hwG+U6AmSL4CCNjqF9QBERAEREAREQHVulzprLbKu4VsvY0dJC+eaXlLuRjWlzjobJ0Ae5Vi7iHkvFbCscyHhMbYKGtuJZV1GT01RARSMe4PfFGAHFzuQBu+mng9NHVh5VJdYcXvD7DFFPfG0czqCKc6jfUBh7IO6joXcu+o6eK6uATZDUYRYpcsgp6XJn0cTrlDSkGJlRyjtAzRI0Hb11P6UBjbTwnx6z8S7znsMdW/JLrTMo5ppqyV8TIWhvqRxF3I0Esa46G97PTZ3MURAEREAREQBERAFVPAm14TbLhxKdht4rbtPU5ZWT3xtYwtFLcSGdtDHuNm2NAbojn7z6xVrKuuEN2+FK3O2+gPoN5tkdVT9t2HZfDegz/OH80zm7Teub198vyygLFREQBERAEREAXVudtp7zbaugq2GSkqoXwTMDywuY5pa4czSCOhPUEFdpEBUx4dZJwnwfHcd4SNthoaG4c1VTZRVVE5NG9zi5kUgJILeYFoPTTAOuzuU2rivj144lXnA4ZaqPJLVTMrJoJ6SRkckLgz145S3keAXtB0d737DqYKGOqs2+ONlM2jovi6+AjI6r2POfhPzjQZrm3ydj1+Trfj4ICZoiIAiIgCIiA058sjyzbvwMvl1waXBZZ6a8Wl5t+Q016MD9SMdG54YIHcr4383QP30aenMNYnyLPLQuvFi9Yzw0bhVVKLdaz5/ktTejUSBkUehNIwwguL5ORvy+hk310rB+6A8EouLfA2quFLFGchxouuNC4/Llj5fv8LfE8zAHADqXRtHisJ9zi4IRcNeDz8mrog3Isoc2oka75cFK3fYRkeBdt0h7vltBG2r2ztcG2yIi8AREQBERAEREBR/lWeUZcvJoxS05FBhvpXbKqqNJVSC4+aGleW80ZI7GTmDuWQb6aLWjrzdNTuFn3TbL7pk9XZqjBnZbX329EWalZdI6U0UMpa2Kk2yl++8p398donfXuW+nFnhxbeLvDm/4hdmg0l1pXQ9oW8xhk745QPax4a4fO1aG/c7/Jhr7RxcynKsqouylxGpltNFG7q19YdtklafymsYeh1o9qCPkr2wPR9EReAIiIAiIgCIiAiPFvNLjw54bZDk9qsfpJWWmlNX8GCp83M0bCDJp/I/RazncBynZbrx2vOp/wB0xrDxjZm/oVWihbYjZjj3pGfNjJ5x2vnP+j659ep8nevHwXqC9jZGOa5oc1w0WkbBC8s6ryKon+W2MPETW4BJKb6ZWOAjbRcxPm2/B3afedb5uXTl6k3oD0d4P5xcOJfDPH8pulhdjNXdqbzv4LdU+cGGNziYiX8jN8zOR+uUa5teG1MV+Y42xRtYxoYxoDWtaNAAdwAX6XgCIiAKH5dllRBWOs9oe1lcGtfU1bmc7KVjt6AHc6UgbAPRo05wILWvllTOylp5ZpDqONpe4/MBsqoMYdJVWiKvn0au4/y2dw31fJ62uvgAQ0fM0BSxtGLqPdku0u4Wiqs/W0R89FrbNOaispxdKw/Kqrh/KJT+gv3yj81ugPADS/Rxe2NmE9PSMoKpuy2povvErSfzmaP7D0UO4v8AFQ8LqzDZJuwZbLrdjQ1sskMkr2R+bzSAxtZ1Ly+Ng1p29ka3pSTCOIOP8RrXLcMduTLjTQyugmAY+OSGQd7JI3gPY7qOjgD1XHP1b32nxNxbHsE6xPK6llbFZ7vKaiaQHzW4FrW9vobLHhoAEmgT0ADgDoDRCmqp/I6eWezVLqdwZWQN84ppD+RMz1o3fo5gNjxGx4q1LPco7zaKG4QgiKrgZOwHwDmhw/xXcrTgqi7H5+PgYmLoqlJOOjO4iIoiiEREAWFynJo8aoGyCE1dZO7sqaka7lMr/nPXlaB1c7R0B0BOgc0qvvtV8K57dHu05trijoYh19Rz2tmkP/EHQj/gClppZyeiV/D6ssUKXO1FF6GPuFodkkhnyGQXWV3dTOBFLEP6LIt6Pzudtx9utAcMmG2KQscLRRxPjGmSQwtjewfmuboj9hWWkkZDG6SRzWMaC5znHQAHeSVDMR4zYdnl4dbLDdzcakMfI1zKSdsMrWkBzo5nMEcgBI6sce9c+kVd0muzI+hUYQSirInliymrxieOmuVQ+ts0jxGyrmcXTUhJ00SOPy4/DnPrN6FxcCXNsdVbU08VXTywTMEkMrSx7HdzmkaIP7FK+G1xluOH0fnEnbVFM+WjkkJJLjDI6PmJPXZDAf2rtvnIbb1Wvxv/AFn/AGZGMoxptTjvJOiIoTNCIiALp3i7U1itlRX1biyngbzO5RzOce4NaB1LiSAAOpJAXcVe8Q6o12TWS1kgwQRS3GVh/KeC2OL9IHNIevi1p7xsSU4qUs9FnwJaUOcmomJur63LXvku8ssdE7+btMUnLGxvh2pafvjvaCSwdwB1zHqjFLII+zFnt4j/AKHmrNf3aWUVNcUfKSsOKdrbLDcaa45JDdaO2y08lLPJAx0lRGyVhlaBH2rWOeeXn2COoOiFy69R6Oy6lofRKNOjHqLbtZrMSeySzyyOo2fzlpkfzRPb49mXfzbvZohh8R4iy7Nd6e+2ynr6UuMEzdgPbyuaQdOa4eDgQQR4EEKvl2+H1UaHKrzbAQIKqGO4RsHhJsxy/oBAiOh4lx7z1kUnWi9rVZ36+0oYyhHZ5yJYaIihMc61ypPhC3VVKTrt4nxb9mwR/wD1VLikjpMatge1zJWU7IpGOGi17RyuB/Q4EK41XGVWR+L3GrukMZfZqt/bVIjaXOpZj8qQgf6t3QuP5LtuO2ucWTRW3B01rqv289RoYOqoTcZbyn+OzK6mvPDe70lnuV5p7Vf3VVXHa6V1RLHF5pUML+UdSNvb853oAkgH88HKCvuvEHiFms1mrrBa786ggo6S5web1Mvm8T2vnfEerOYvDQHacRGCQOitmKaOoiZLE9skbwHNew7Dge4gr697Y2Oe9wYxo2XOOgB7SqvwNnY9bav5tY6V9rRbbLX1RBd2MD38rRskhp0APEnu0rHxW1PsWL2e2yaL6Ojhp3a9rGBp/wAFB8btBzKtpawhrrBTSNnZLs6rJWkOZy+BjadO5u5xA10B3ZqtNc3DYet7vw8THxtVTkox3BERQmcEREAVV11O6izrJongjzmWCuZsdC10DIun/FA5WoonnGM1FyNPdbbG2W6UbXMELnBoqYXEF0ez0DttBaT0B2CQHEiam77UOtW70/AtYaoqVRN6Fa8U8crcw4aZXYrbKIbhcrXU0lO9zuUCR8Tmt2fAbI2VGeEWfPuFssuNzYXkeO1dDQMgqPPraYaOndExreRk2+WQEj1SzYIHXSsWguUFxZIYi5skTuzmhkaWyQv/AKL2nq09QdHwIPcQuyq0ouL2ZLM+gtd7SYWe4VQFmHsqCHAVlVU1TeYaPI+Z5Yf2t5T+1Ranop8xqZbZQF7aRrjHXXBvRkTe50cbvGUjY6dGdS7qGtdalLTRUVNFTwRthgiYI442DQa0DQAHsAVmzp09l6u3BfvcysdVTtBHKiIoTKCIiAKuM5gNLndsqSD2dXb5YA7XQPjka4A/OQ9xH9Uqx1hMuxxuS2nsGvbDWQPFRSTuGxFM0EAkDvaQXNcPFrnDpvalptJ2ejyJqM+bqKTIYtRhSX6zcHaThtPhmRyZBb8ippp7hTW18tHVxi6MnNUJ27DgWHZHygd7AAJG19PXbq5aCqj80ucA3NSPOyB3c7Toc7D4OA14HRBA7SglGUHaSPoZRVRXT8sLmwinNVn1fUtB5KO3MgJ10LpZC7X6QIgT/WHtWPqK8Nq4qGmZ53c5xuGkY71nDei53fysHi49B85IBnuI44MatRie9s1bUSGoq52AgSSkAHQPc0BrWtH9FrfFT006cXN71ZeL7NxUxlVRhsb2ZtERRGGEREBVvF3DLVYuHuYZDaYZbRdKK1VldFJQTvgjMzIXva50bSGOPMASS3Z6+0rFeT9jdDmvB/CMov7Zbxd7laqetndWTPki7V8Yc4iInkHXu6dFMeOH/YrxA/3fuH/tpFgvJY/7t3DP/d+j/wCk1T8/V95knOTta+RaaIigIwiIgCIiAIiIDB3/AAmy5NOye4UIkqWN5G1MMj4Zg3vA7Rha7Wyem/E+1UT5O9vj4iXPilDkU1XdYcezKustvimrJuRlLEGFjXNDgJD6x9Z4J+dbJLXfyQ/wzx2/WPdPqxKZVqsVZSfE7U5JWTNgqOjp7dSxU1LBHTU0TQyOGFgYxjR3AAdAFzIiibvmzgIiLwBERAEREBjL7jdsyWnZDc6KKraw80bnjT4z7WOGnNPzggrX+SikZ5XcOAC5XMYu7CjfTS+fS8/nQrux32nNz8vJ05d/Otk1rtN/4g9P+rJ3/wAopY1akVZSyO4zlH2XYvaxY1bMap3w2yjjpWvO5HN2XyH2veducfnJKyaIuJScneTuzlu+bCIi5PAiIgITxw/7FeIH+79w/wDbSLBeSx/3buGf+79H/wBJqnWaY2zMsOvtgkndSx3WgnoXTtbzGMSxuYXAeOubelrdjudcRfJPx6145m+Hsyvh5aKWOjpcrw+N756aGNoaHVdK9xcDobc9h5R4bPRAbVIovw84oYpxXsTLxiV9o77QHXM+lk26In8mRh05jvzXAH5lKEAREQBERAEREAWu/kh/hnjt+se6fViWxC138kP8M8dv1j3T6sSA2IREQBERAEREAREQBa7Tf+IPT/qyd/8AKLM8RPKzxfF767FsUo6ziTnB21thxsdt2RHQmecbZC0HvJ2W+LV0OD/C3iFcuMNVxb4kzWi13iexGw0eN2ZrpWUlMZ2zgyzl2nyBwcDygtPN0I1pAX+iIgCIiAIiIAiIgKN4h+SXjGSX1+U4hXVnDTOBtzb5jh7Jsx79VEHRkrSepHQu8SVF28d+JXAZwpuMuL/DmOR+qM8xKF0sLW/0qulA5ovnc0cu+jQVs0vjmhzS1wBBGiD4oDA4Tn2OcSLFDesXvVHfLZL3VFHKHgH+i4d7XDxa4AjxCz6oTNfJHsc99lynhzd6zhXmTvWdXWEAUlUe/VRSH73I3fU61snZ2sJF5RWc8EpWUPG/EyLQCGMzvFo31NvcO4OqIQO0gPtOiCT6rdDaA2WRYfFMwsedWSC8Y7dqO9Wucbjq6GZssZ9o2D0I8Qeo8VmEAREQBa7+SH+GeO36x7p9WJYv7oFwMPGDgfUXK3wCXIMWL7lS6G3SQcv8oiH6WtD9DqTE0eK83/JL4ISceuNtlsE0L3WSmd5/dnjoG0sZBc3fgXuLYx7OffggPbxERAEREARVFxV8qDCuFtybYu2qcozGY8lPjGPRGrrpH+Ac1vSP2+uQddQCoH6B8aPKE++ZveTwlwyXr6NY3OJLrUsP5NRV61Hsd7WDqCQ5oPVATPih5VWH8Prz6N2xtXnGbyEtixnG4/OakO//AClvqxAdN8x2B15SoX8U/Fzygvv3E/IDw+xGXr6GYpUbqZmH8mrrPH2FrPVIP5JVzcMODWGcGrN8GYfYKWzwuA7aaNvNPOfbJK7b3n9JOvDSmiAivDvhbifCaxNs+I2GjsVCNczaZnrykflSPO3SO+dxJUqREAREQBERAEREAREQBERAF+JYmTxPilY2SN7S1zHjYcD3gjxC/aIDX7K/JHt1vvc+T8KL7VcKsqkPPJ8FND7ZWEdeWejPqEf1da3vRKxUXlO5RwakbQcdcSks1I08keaY3FJWWmf2GRjQZYXHwBBJPcAFssqI8sXhHnnHThczC8KrrNbaeuqmS3Wou9RIztIYyHxwsayCQ9ZAx5cHMI7ID1g92gKT8jzy86vi3xOvmJ5q+Kjdd62WqxxzgwdgwuJbQOe1jA8tbrke5oc4hwJJc0LeJzgxpc4hrQNkk9AF5YUv3LTjBQ1UNTTZPh9PUQvEkc0VfWNexwOw4EU2wQeuwt57ZVZbVY1ZMXzSqoKu+UNI11+qbTI51PWSbc2JnrMYdOYBJI3laNkNG2Eg9wjtXvotSWnTdWSiiW3HiW6sLo7BaxdYD08+qp+wpXj8whrnSD5w3lPg4+FM8BOEcfk7XDLq2x2egrDkFcap8bKp0bqWEEmOmi3HosZzv0TonYBPQFWoi956KyUFb5/v9LG0sHSSs8yZY5l9DkvaxRCWlrYRuaiqmckrB7R4Ob+c0keG9ggY7irxNsnB7AbvluQT9jbrfEX8jSOeZ56MiYD3uc4gD9OzoAlRStp5nmOpo5fNrjTnnp5gSAHf0Xa72O0A5viPYQCKK8rHyfeKHlcMxmoxm/4/bMOpqYTm1XOeeKZlftzJefs4pGycgHIHbGtvAHXbvWlKO3H+vPn45mIocy8tGQfydfukdZfLtltJxAoK25VdVOKnG7bj1t7ad2yWuohykF5A5HMc4bOpeZ/yGq7fRvjd5QvrZDcHcF8Jl/8AKLPKJr5VRnwlqNcsGx4NHMOocCtauGP3OTjRwxz2y5Xa8sxCkuNon87gdHWVrmyvaCRDIGwsJik/m5AHD1Hv6HuPpaoimQPhVwNwjgrbXUmJWGnt0ko/lFc7ctXUnvJkmdtzuvXW9DfQBTxEQBERAEREAREQBERAEREAREQBERAEREAREQBVLA90uR5RI/8AnDcy09OumwxNb/ygf3q2lWeTULrHmU0zgRR3kNfG8noKljOVzP0ujY1w9vI/2KaGcJxWtvo/L+RewclGrnvIVxlz2ThhwvyPKIKZtXUW6lMkMMh0x0hIazm/N5nAn5gVAeG164ptzi1097or5X2CrilFwqb1Q22lbRyBnNG6DzWoe5zXOHIWvDiOYHm6FXDkOP2/K7FX2a7UrK2210Lqeop5N6exw0R06j9I6jwUXwLhWzAqsSx5Tkt7gjp/NaejvFc2aGnj20gNa1jeYjlADnlzgNjfUqobUoyc075E4WX4UPd8FXiL/VRXWcR67tO5Xu/53P8A27WBrq2O30klRLstYPktG3PJ6BrR4uJIAHiSAprgljnx/GKWnqwBXSF9TVAO5g2WRxe9oPiGl3KD7GhWqeVKTe+3nz1lHHSWwo7yQIiKIxQiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgC6V3tFJfbfLRVsQlgk103pzSDsOaR1a4EAhw6ggELuovU2ndDQrGvx3IrC8tZSHIqMfJnp3xxVQH58bi1hP5zHDfgwdyjOLZuM6mu8GO2mtuNRaa6S21weY4WU1SzXPG9zn945hvlDu/xV6LXfyQ/wzx2/WPdPqxKXbg83BX+a8fpYvLGVUrFr43hE8NZHcb3JBUVUZ5qekgaTDTH+lzHq9/52mgA6AHUmYoi4lJy1Kk5yqPakwiIuDgIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgC138kP8M8dv1j3T6sS2IWu/kh/hnjt+se6fViQGxCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiALXfyQ/wAM8dv1j3T6sSzHlWeUZcvJoxS05FBhvpXbKqqNJVSC4+aGleW80ZI7GTmDuWQb6aLWjrzdNLPJ98vWvxfLcptlq4bOv9zznLJrrTUzL12JhkqSxjYN+bu59Fo9f1d77hpAeoaIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIDq1t0orYGGsq4KQP3y9vK1nNrv1s9V1fSqy++KD6Sz7VCuJdHBW5ljEdRBHPGKSuPLKwOG+am66K6Ho9a/dtH+4Z9i4q1qVDZU022r7utrwMfF8pRwtTm3G/zLE9KrL74oPpLPtT0qsvvig+ks+1V36PWv3bR/uGfYno9a/dtH+4Z9ir+m0PdfcU+m4fhvj/Bl+LNhxPi7w5v+IXa6280l1pXQ9oZ43GGTvjlA33seGuHztWin3PjyfG41xbyXK8ydTUUmKzyWy3snmaGTVZ22SZhJHMxrD0drR7UEH1Vup6PWv3bR/uGfYno9a/dtH+4Z9iem0PdfcOm4fhvj/BYnpVZffFB9JZ9qelVl98UH0ln2qu/R61+7aP8AcM+xPR61+7aP9wz7E9Noe6+4dNw/DfH+CxPSqy++KD6Sz7U9KrL74oPpLPtVd+j1r920f7hn2LC5rYrbFht+ey30rHtoKgtc2BoIPZu6jopaWKoVKkYWebS3bzuHLMJyUdh5/H+C8QdjY7l9XFTf6NF/UH+C5VMfQhERAEREAREQBERAEREAREQBERAEREBXnEL8dsY/sdf9amXEuXiF+O2Mf2Ov+tTLiWZyh7UP0/8AqR8Vyv8Aefkguje75b8atFXdLrWQ0FupIzLPUzvDWRtHiSu8qt8pLELtmnC2opLNBPXVdLW0lwdQU05glq44Z2SPiZICC15DSWkEHmA0QVmRSbSZkU4qU1GTsmZO3cecGulrvNwhvZZBZ6Q11aypo6iCaKnG9yiJ8bXuZ0PrNaQu/inFvE82vM1qs12FVXRwedCN9PLEJoOYN7WJz2NbLHsgc8Zc3qOvVUdfcRtOVcPOIVxsWMcQPSBuM1Vvpn5TJXTSzCZpc6CCOeR7nO5o2E8rdEkaJ6qU8RcKveQ5XhFPbaWppnPxG926SvbE4R0s0sNM2ISPA0w8wcQD19U67lNsQ0Ljo0tLta62ysrmRuvlJ2G45ph9gxO4013fdLy631kjqWfs+xbDM57oJtNjeQ9jGktLwOY9OoKudavWGtul3peCOOtwXI7JVYxc4WXM1Fre2kp+zoZ4nObMNtexzyCHgkdRsgkA7QripFRskRYiEYbKivN2Fg85/EnIP9n1H/Tcs4sHnP4k5B/s+o/6blLhfvFPtX1IqP2se1Fv03+jRf1B/guVcVN/o0X9Qf4LlW+9T9KCIi8AREQBERAEREAREQBERAEREAREQFecQvx2xj+x1/1qZRzKsDxzOYqePIrFbr5HTkuhbcKZkwjJ1st5gdb0P7lY2T4VRZVU0VRUz1dNPSNkZFJSTdmeV/LzA9OvyG/3LE/FVQ++L39N/goq+HVdxkp2aVtH1t+JhYzk+pia3Owkll8Srv8AJ/4Z6I9Acc0euvgyHX1VmMX4X4hhFdJW4/jFpslZJGYXz0FHHC9zCQS0loBI20HXzBTn4qqH3xe/pv8ABPiqoffF7+m/wVb0G+tXuZSfJOIas6i7zGosl8VVD74vf03+CfFVQ++L39N/guej1+IuDI+havvrvMNW0VPcqKopKuGOppaiN0UsMrQ5kjHDTmuB6EEEghQhnAHhpG9r2YDjjXtOw5tshBB9vyVaHxVUPvi9/Tf4J8VVD74vf03+C9WAtpV7mdx5Irx9molxKt/yfuGX/oDG/wD9XD/9VJM46YTkH+zqj/puUu+Kqh98Xv6b/BcVXwgtddSzU1RdLzLBMx0cjHVvRzSNEHp7CpqWDUKkZyqXs09Gdx5Jr7cZSqJ2fxJtTf6NF/UH+C5V+WNDGNaO4DQX6Vk+oCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgCIiAIiIAiIgP/9k="
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import * as tslab from \"tslab\";\n",
        "\n",
        "const representation2 = graph2.getGraph();\n",
        "const image2 = await representation2.drawMermaidPng();\n",
        "const arrayBuffer2 = await image2.arrayBuffer();\n",
        "\n",
        "await tslab.display.png(new Uint8Array(arrayBuffer2));"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Adding I'm A to \n",
            "Adding I'm B to I'm A\n",
            "Adding I'm C to I'm A\n",
            "Adding I'm E to I'm A,I'm B,I'm C\n",
            "Result 1:  { aggregate: [ \"I'm A\", \"I'm B\", \"I'm C\", \"I'm E\" ], which: 'bc' }\n"
          ]
        }
      ],
      "source": [
        "// Invoke the graph\n",
        "let g2result = await graph2.invoke({ aggregate: [], which: \"bc\" });\n",
        "console.log(\"Result 1: \", g2result);"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Adding I'm A to \n",
            "Adding I'm C to I'm A\n",
            "Adding I'm D to I'm A\n",
            "Adding I'm E to I'm A,I'm C,I'm D\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Result 2:  { aggregate: [ \"I'm A\", \"I'm C\", \"I'm D\", \"I'm E\" ], which: 'cd' }\n"
          ]
        }
      ],
      "source": [
        "g2result = await graph2.invoke({ aggregate: [], which: \"cd\" });\n",
        "console.log(\"Result 2: \", g2result);\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Stable Sorting\n",
        "\n",
        "When fanned out, nodes are run in parallel as a single \"superstep\". The updates\n",
        "from each superstep are all applied to the state in sequence once the superstep\n",
        "has completed.\n",
        "\n",
        "If you need consistent, predetermined ordering of updates from a parallel\n",
        "superstep, you should write the outputs (along with an identifying key) to a\n",
        "separate field in your state, then combine them in the \"sink\" node by adding\n",
        "regular `edge`s from each of the fanout nodes to the rendezvous point.\n",
        "\n",
        "For instance, suppose I want to order the outputs of the parallel step by\n",
        "\"reliability\".\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Adding I'm A to \n",
            "Adding I'm B to I'm A\n",
            "Adding I'm C to I'm A\n",
            "Result 1:  {\n",
            "  aggregate: [ \"I'm A\", \"I'm C\", \"I'm B\", \"I'm E\" ],\n",
            "  which: 'bc',\n",
            "  fanoutValues: []\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "type ScoredValue = {\n",
        "  value: string;\n",
        "  score: number;\n",
        "};\n",
        "\n",
        "const reduceFanouts = (left?: ScoredValue[], right?: ScoredValue[]) => {\n",
        "  if (!left) {\n",
        "    left = [];\n",
        "  }\n",
        "  if (!right || right?.length === 0) {\n",
        "    // Overwrite. Similar to redux.\n",
        "    return [];\n",
        "  }\n",
        "  return left.concat(right);\n",
        "};\n",
        "\n",
        "const StableSortingAnnotation = Annotation.Root({\n",
        "  aggregate: Annotation<string[]>({\n",
        "    reducer: (x, y) => x.concat(y),\n",
        "  }),\n",
        "  which: Annotation<string>({\n",
        "    reducer: (x: string, y: string) => (y ?? x),\n",
        "  }),\n",
        "  fanoutValues: Annotation<ScoredValue[]>({\n",
        "    reducer: reduceFanouts,\n",
        "  }),\n",
        "})\n",
        "\n",
        "\n",
        "class ParallelReturnNodeValue {\n",
        "  private _value: string;\n",
        "  private _score: number;\n",
        "\n",
        "  constructor(nodeSecret: string, score: number) {\n",
        "    this._value = nodeSecret;\n",
        "    this._score = score;\n",
        "  }\n",
        "\n",
        "  public call(state: typeof StableSortingAnnotation.State) {\n",
        "    console.log(`Adding ${this._value} to ${state.aggregate}`);\n",
        "    return { fanoutValues: [{ value: this._value, score: this._score }] };\n",
        "  }\n",
        "}\n",
        "\n",
        "// Create the graph\n",
        "\n",
        "const nodeA3 = (state: typeof StableSortingAnnotation.State) => {\n",
        "  console.log(`Adding I'm A to ${state.aggregate}`);\n",
        "  return { aggregate: [\"I'm A\"] };\n",
        "};\n",
        "\n",
        "const nodeB3 = new ParallelReturnNodeValue(\"I'm B\", 0.1);\n",
        "const nodeC3 = new ParallelReturnNodeValue(\"I'm C\", 0.9);\n",
        "const nodeD3 = new ParallelReturnNodeValue(\"I'm D\", 0.3);\n",
        "\n",
        "const aggregateFanouts = (state: typeof StableSortingAnnotation.State) => {\n",
        "  // Sort by score (reversed)\n",
        "  state.fanoutValues.sort((a, b) => b.score - a.score);\n",
        "  return {\n",
        "    aggregate: state.fanoutValues.map((v) => v.value).concat([\"I'm E\"]),\n",
        "    fanoutValues: [],\n",
        "  };\n",
        "};\n",
        "\n",
        "// Define the route function\n",
        "function routeBCOrCD(state: typeof StableSortingAnnotation.State): string[] {\n",
        "  if (state.which === \"cd\") {\n",
        "    return [\"c\", \"d\"];\n",
        "  }\n",
        "  return [\"b\", \"c\"];\n",
        "}\n",
        "\n",
        "const builder3 = new StateGraph(StableSortingAnnotation)\n",
        "  .addNode(\"a\", nodeA3)\n",
        "  .addEdge(START, \"a\")\n",
        "  .addNode(\"b\", nodeB3.call.bind(nodeB3))\n",
        "  .addNode(\"c\", nodeC3.call.bind(nodeC3))\n",
        "  .addNode(\"d\", nodeD3.call.bind(nodeD3))\n",
        "  .addNode(\"e\", aggregateFanouts)\n",
        "  .addConditionalEdges(\"a\", routeBCOrCD, [\"b\", \"c\", \"d\"])\n",
        "  .addEdge(\"b\", \"e\")\n",
        "  .addEdge(\"c\", \"e\")\n",
        "  .addEdge(\"d\", \"e\")\n",
        "  .addEdge(\"e\", END);\n",
        "\n",
        "const graph3 = builder3.compile();\n",
        "\n",
        "// Invoke the graph\n",
        "let g3result = await graph3.invoke({ aggregate: [], which: \"bc\" });\n",
        "console.log(\"Result 1: \", g3result);\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Our aggregateFanouts \"sink\" node in this case took the mapped values and then\n",
        "sorted them in a consistent way. Notice that, because it returns an empty array\n",
        "for `fanoutValues`, our `reduceFanouts` reducer function decided to overwrite\n",
        "the previous values in the state.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Adding I'm A to \n",
            "Adding I'm C to I'm A\n",
            "Adding I'm D to I'm A\n",
            "Result 2:  {\n",
            "  aggregate: [ \"I'm A\", \"I'm C\", \"I'm D\", \"I'm E\" ],\n",
            "  which: 'cd',\n",
            "  fanoutValues: []\n",
            "}\n"
          ]
        }
      ],
      "source": [
        "let g3result2 = await graph3.invoke({ aggregate: [], which: \"cd\" });\n",
        "console.log(\"Result 2: \", g3result2);\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "TypeScript",
      "language": "typescript",
      "name": "tslab"
    },
    "language_info": {
      "codemirror_mode": {
        "mode": "typescript",
        "name": "javascript",
        "typescript": true
      },
      "file_extension": ".ts",
      "mimetype": "text/typescript",
      "name": "typescript",
      "version": "3.7.2"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}
